package com.hou.serviceorder.config;

import ch.qos.logback.core.rolling.helper.IntegerTokenConverter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;

/**
 * 限流切面
 */
@Aspect
@Order(value = Ordered.HIGHEST_PRECEDENCE) //最高优先级
@Component
public class ApiLimitAspect {
    //存储限流量和方法,必须是static且线程安全,保证所有线程进入都唯一
    public static Map<String, Semaphore> semaphoreMap= new ConcurrentHashMap<>();
    //拦截所有controller 的所有方法
    @Around("execution(* com.hou.serviceorder.controller.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint){
        Object result=null;
        Semaphore semaphore=null;
        Class<?> clz = joinPoint.getTarget().getClass();//获取目标对象
        Signature signature = joinPoint.getSignature();//获取增强方法信息
        String name = signature.getName();
        String limitKey = String.valueOf(getLimitKey(clz, name));
        if(limitKey!=null && !"".equals(limitKey)){
            semaphore = semaphoreMap.get(limitKey);
            try {
                semaphore.acquire();
                result=joinPoint.proceed();//执行目标方法
            } catch (Throwable e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
            }
        }
        return result;
    }

    //获取拦截方法配置的限流key,没有返回null
    private Integer getLimitKey(Class<?> clz, String methodName){
        for (Method method:clz.getDeclaredMethods()){
            if(method.getName().equals(methodName)){//找出目标方法
                if(method.isAnnotationPresent(ApiRateLimit.class)){//判断是否是限流方法
                    return method.getAnnotation(ApiRateLimit.class).value();
                }
            }
        }
        return null;
    }
}
